{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 物品冷启动-利用物品的内容信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入包\n",
    "import random\n",
    "import math\n",
    "import numpy as np\n",
    "import time\n",
    "from tqdm import tqdm, trange"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 一. 通用函数定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义装饰器，监控运行时间\n",
    "def timmer(func):\n",
    "    def wrapper(*args, **kwargs):\n",
    "        start_time = time.time()\n",
    "        res = func(*args, **kwargs)\n",
    "        stop_time = time.time()\n",
    "        print('Func %s, run time: %s' % (func.__name__, stop_time - start_time))\n",
    "        return res\n",
    "    return wrapper"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 数据处理相关\n",
    "1. load data\n",
    "2. split data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dataset():\n",
    "    \n",
    "    def __init__(self, fp, ip):\n",
    "        # fp: data file path\n",
    "        self.data, self.content = self.loadData(fp, ip)\n",
    "    \n",
    "    @timmer\n",
    "    def loadData(self, fp, ip):\n",
    "        data = []\n",
    "        for l in open(fp):\n",
    "            data.append(tuple(map(int, l.strip().split('::')[:2])))\n",
    "        contents = {}\n",
    "        for l in open(ip, 'rb'):\n",
    "            l = str(l)[2:-1]\n",
    "            contents[int(l.strip().split('::')[0])] = l.strip().split('::')[-1].split('|')\n",
    "        return data, contents\n",
    "    \n",
    "    @timmer\n",
    "    def splitData(self, M, k, seed=1):\n",
    "        '''\n",
    "        :params: data, 加载的所有(user, item)数据条目\n",
    "        :params: M, 划分的数目，最后需要取M折的平均\n",
    "        :params: k, 本次是第几次划分，k~[0, M)\n",
    "        :params: seed, random的种子数，对于不同的k应设置成一样的\n",
    "        :return: train, test\n",
    "        '''\n",
    "        train, test = [], []\n",
    "        random.seed(seed)\n",
    "        for user, item in self.data:\n",
    "            # 这里与书中的不一致，本人认为取M-1较为合理，因randint是左右都覆盖的\n",
    "            if random.randint(0, M-1) == k:  \n",
    "                test.append((user, item))\n",
    "            else:\n",
    "                train.append((user, item))\n",
    "\n",
    "        # 处理成字典的形式，user->set(items)\n",
    "        def convert_dict(data):\n",
    "            data_dict = {}\n",
    "            for user, item in data:\n",
    "                if user not in data_dict:\n",
    "                    data_dict[user] = set()\n",
    "                data_dict[user].add(item)\n",
    "            data_dict = {k: list(data_dict[k]) for k in data_dict}\n",
    "            return data_dict\n",
    "\n",
    "        return convert_dict(train), convert_dict(test), self.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. 评价指标\n",
    "1. Precision\n",
    "2. Recall\n",
    "3. Coverage\n",
    "4. Popularity(Novelty)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Metric():\n",
    "    \n",
    "    def __init__(self, train, test, GetRecommendation):\n",
    "        '''\n",
    "        :params: train, 训练数据\n",
    "        :params: test, 测试数据\n",
    "        :params: GetRecommendation, 为某个用户获取推荐物品的接口函数\n",
    "        '''\n",
    "        self.train = train\n",
    "        self.test = test\n",
    "        self.GetRecommendation = GetRecommendation\n",
    "        self.recs = self.getRec()\n",
    "        \n",
    "    # 为test中的每个用户进行推荐\n",
    "    def getRec(self):\n",
    "        recs = {}\n",
    "        for user in self.test:\n",
    "            rank = self.GetRecommendation(user)\n",
    "            recs[user] = rank\n",
    "        return recs\n",
    "        \n",
    "    # 定义精确率指标计算方式\n",
    "    def precision(self):\n",
    "        all, hit = 0, 0\n",
    "        for user in self.test:\n",
    "            test_items = set(self.test[user])\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                if item in test_items:\n",
    "                    hit += 1\n",
    "            all += len(rank)\n",
    "        return round(hit / all * 100, 2)\n",
    "    \n",
    "    # 定义召回率指标计算方式\n",
    "    def recall(self):\n",
    "        all, hit = 0, 0\n",
    "        for user in self.test:\n",
    "            test_items = set(self.test[user])\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                if item in test_items:\n",
    "                    hit += 1\n",
    "            all += len(test_items)\n",
    "        return round(hit / all * 100, 2)\n",
    "    \n",
    "    # 定义覆盖率指标计算方式\n",
    "    def coverage(self):\n",
    "        all_item, recom_item = set(), set()\n",
    "        for user in self.test:\n",
    "            for item in self.train[user]:\n",
    "                all_item.add(item)\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                recom_item.add(item)\n",
    "        return round(len(recom_item) / len(all_item) * 100, 2)\n",
    "    \n",
    "    # 定义新颖度指标计算方式\n",
    "    def popularity(self):\n",
    "        # 计算物品的流行度\n",
    "        item_pop = {}\n",
    "        for user in self.train:\n",
    "            for item in self.train[user]:\n",
    "                if item not in item_pop:\n",
    "                    item_pop[item] = 0\n",
    "                item_pop[item] += 1\n",
    "\n",
    "        num, pop = 0, 0\n",
    "        for user in self.test:\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                if item in item_pop:\n",
    "                    # 取对数，防止因长尾问题带来的被流行物品所主导\n",
    "                    pop += math.log(1 + item_pop[item])\n",
    "                    num += 1\n",
    "        return round(pop / num, 6)\n",
    "    \n",
    "    def eval(self):\n",
    "        metric = {'Precision': self.precision(),\n",
    "                  'Recall': self.recall(),\n",
    "                  'Coverage': self.coverage(),\n",
    "                  'Popularity': self.popularity()}\n",
    "        print('Metric:', metric)\n",
    "        return metric"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二. ContentItemKNN算法实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def ContentItemKNN(train, content, K, N):\n",
    "    '''\n",
    "    :params: train, 训练数据\n",
    "    :params: content, 物品内容信息\n",
    "    :params: K, 取相似Top-K相似物品\n",
    "    :params: N, 推荐TopN物品的个数\n",
    "    :return: GetRecommendation, 获取推荐结果的接口\n",
    "    '''\n",
    "    \n",
    "    # 建立word-item倒排表\n",
    "    word_item = {}\n",
    "    for item in content:\n",
    "        for word in content[item]:\n",
    "            if word not in word_item:\n",
    "                word_item[word] = {}\n",
    "            word_item[word][item] = 1\n",
    "            \n",
    "    for word in word_item:\n",
    "        for item in word_item[word]:\n",
    "            word_item[word][item] /= math.log(1 + len(word_item[word]))\n",
    "\n",
    "    # 计算相似度\n",
    "    item_sim = {}\n",
    "    mo = {}\n",
    "    for word in word_item:\n",
    "        for u in word_item[word]:\n",
    "            if u not in item_sim:\n",
    "                item_sim[u] = {}\n",
    "                mo[u] = 0\n",
    "            mo[u] += word_item[word][u] ** 2\n",
    "            for v in word_item[word]:\n",
    "                if u == v: continue\n",
    "                if v not in item_sim[u]:\n",
    "                    item_sim[u][v] = 0\n",
    "                item_sim[u][v] += word_item[word][u] * word_item[word][v]\n",
    "    for u in item_sim:\n",
    "        for v in item_sim[u]:\n",
    "            item_sim[u][v] /= math.sqrt(mo[u] * mo[v])\n",
    "                \n",
    "    # 按照相似度排序\n",
    "    sorted_item_sim = {k: list(sorted(v.items(), \\\n",
    "                               key=lambda x: x[1], reverse=True)) \\\n",
    "                       for k, v in item_sim.items()}\n",
    "        \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        items = {}\n",
    "        seen_items = set(train[user])\n",
    "        for item in train[user]:\n",
    "            for u, _ in sorted_item_sim[item][:K]:\n",
    "                # 要去掉用户见过的\n",
    "                if u not in seen_items:\n",
    "                    if u not in items:\n",
    "                        items[u] = 0\n",
    "                    items[u] += item_sim[item][u]\n",
    "        recs = list(sorted(items.items(), key=lambda x: x[1], reverse=True))[:N]\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 三. ContentItemKNN实验\n",
    "M=8, N=10, K=10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Experiment():\n",
    "    \n",
    "    def __init__(self, M, N, K, fp='../dataset/ml-1m/ratings.dat', ip='../dataset/ml-1m/movies.dat'):\n",
    "        '''\n",
    "        :params: M, 进行多少次实验\n",
    "        :params: N, TopN推荐物品的个数\n",
    "        :params: K, 取Top-K相似物品数目\n",
    "        :params: fp, 数据文件路径\n",
    "        :params: ip, 物品内容路径\n",
    "        '''\n",
    "        self.M = M\n",
    "        self.K = K\n",
    "        self.N = N\n",
    "        self.fp = fp\n",
    "        self.ip = ip\n",
    "        self.alg = ContentItemKNN\n",
    "    \n",
    "    # 定义单次实验\n",
    "    @timmer\n",
    "    def worker(self, train, test, content):\n",
    "        '''\n",
    "        :params: train, 训练数据集\n",
    "        :params: test, 测试数据集\n",
    "        :return: 各指标的值\n",
    "        '''\n",
    "        getRecommendation = self.alg(train, content, self.K, self.N)\n",
    "        metric = Metric(train, test, getRecommendation)\n",
    "        return metric.eval()\n",
    "    \n",
    "    # 多次实验取平均\n",
    "    @timmer\n",
    "    def run(self):\n",
    "        metrics = {'Precision': 0, 'Recall': 0, \n",
    "                   'Coverage': 0, 'Popularity': 0}\n",
    "        dataset = Dataset(self.fp, self.ip)\n",
    "        for ii in range(self.M):\n",
    "            train, test, content = dataset.splitData(self.M, ii)\n",
    "            print('Experiment {}:'.format(ii))\n",
    "            metric = self.worker(train, test, content)\n",
    "            metrics = {k: metrics[k]+metric[k] for k in metrics}\n",
    "        metrics = {k: metrics[k] / self.M for k in metrics}\n",
    "        print('Average Result (M={}, N={}, K={}): {}'.format(\\\n",
    "                              self.M, self.N, self.K, metrics))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Func loadData, run time: 1.8746070861816406\n",
      "Func splitData, run time: 2.308419942855835\n",
      "Experiment 0:\n",
      "Metric: {'Precision': 1.65, 'Recall': 0.79, 'Coverage': 17.13, 'Popularity': 4.616362}\n",
      "Func worker, run time: 8.00853180885315\n",
      "Func splitData, run time: 2.2228479385375977\n",
      "Experiment 1:\n",
      "Metric: {'Precision': 1.81, 'Recall': 0.87, 'Coverage': 16.81, 'Popularity': 4.611126}\n",
      "Func worker, run time: 6.897377967834473\n",
      "Func splitData, run time: 1.8748629093170166\n",
      "Experiment 2:\n",
      "Metric: {'Precision': 1.9, 'Recall': 0.91, 'Coverage': 16.29, 'Popularity': 4.621031}\n",
      "Func worker, run time: 6.969089031219482\n",
      "Func splitData, run time: 1.9319438934326172\n",
      "Experiment 3:\n",
      "Metric: {'Precision': 1.74, 'Recall': 0.84, 'Coverage': 16.63, 'Popularity': 4.623422}\n",
      "Func worker, run time: 7.836109161376953\n",
      "Func splitData, run time: 1.8041598796844482\n",
      "Experiment 4:\n",
      "Metric: {'Precision': 1.79, 'Recall': 0.86, 'Coverage': 16.31, 'Popularity': 4.619975}\n",
      "Func worker, run time: 9.520605087280273\n",
      "Func splitData, run time: 1.8084988594055176\n",
      "Experiment 5:\n",
      "Metric: {'Precision': 1.84, 'Recall': 0.89, 'Coverage': 16.47, 'Popularity': 4.638201}\n",
      "Func worker, run time: 6.670089960098267\n",
      "Func splitData, run time: 1.8033149242401123\n",
      "Experiment 6:\n",
      "Metric: {'Precision': 1.82, 'Recall': 0.88, 'Coverage': 17.13, 'Popularity': 4.607126}\n",
      "Func worker, run time: 6.848791122436523\n",
      "Func splitData, run time: 1.8024988174438477\n",
      "Experiment 7:\n",
      "Metric: {'Precision': 1.84, 'Recall': 0.88, 'Coverage': 16.64, 'Popularity': 4.607283}\n",
      "Func worker, run time: 6.614134073257446\n",
      "Average Result (M=8, N=10, K=10): {'Precision': 1.79875, 'Recall': 0.865, 'Coverage': 16.67625, 'Popularity': 4.61806575}\n",
      "Func run, run time: 76.95174098014832\n"
     ]
    }
   ],
   "source": [
    "M, N, K = 8, 10, 10\n",
    "exp = Experiment(M, N, K)\n",
    "exp.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 四. 实验结果\n",
    "1. Random实验（复制第二章的结果）   \n",
    "\n",
    "    Running time: 185.54872608184814\n",
    "    \n",
    "    Average Result (M=8, K=0, N=10): \n",
    "    {'Precision': 0.61, 'Recall': 0.29, \n",
    "     'Coverage': 100.0, 'Popularity': 4.38958}\n",
    " \n",
    "2. MostPopular实验（复制第二章的结果）\n",
    "\n",
    "    Running time: 103.3697898387909\n",
    "    \n",
    "    Average Result (M=8, K=0, N=10): \n",
    "    {'Precision': 12.83, 'Recall': 6.16, \n",
    "    'Coverage': 2.43, 'Popularity': 7.72326}\n",
    "    \n",
    "3. ItemCF实验（复制第二章的结果）\n",
    "\n",
    "    Running time: 835.2476677894592\n",
    "    \n",
    "    Average Result (M=8, K=10, N=10): \n",
    "    {'Precision': 22.17, 'Recall': 10.65, \n",
    "     'Coverage': 19.11, 'Popularity': 7.2495425}\n",
    "\n",
    "4. ContentItemKNN实验\n",
    "\n",
    "    Running time: 76.9517409801483\n",
    "    \n",
    "    Average Result (M=8, N=10, K=10): {'Precision': 1.80, 'Recall': 0.865, 'Coverage': 16.676, 'Popularity': 4.61807}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 五. 问题\n",
    "1. 实验结果与书中的不符合(大多数指标明显偏低)，不知道是否是实现错误。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 附录：日志（请双击查看）\n",
    "1. Random实验（复制第二章的结果）\n",
    "Func loadData, run time: 1.40358304977417\n",
    "Func splitData, run time: 2.1179611682891846\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 0.61, 'Recall': 0.29, 'Coverage': 100.0, 'Popularity': 4.3952}\n",
    "Func worker, run time: 20.80728793144226\n",
    "Func splitData, run time: 2.039689064025879\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 0.61, 'Recall': 0.29, 'Coverage': 100.03, 'Popularity': 4.384244}\n",
    "Func worker, run time: 22.058059692382812\n",
    "Func splitData, run time: 2.129431962966919\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 0.64, 'Recall': 0.3, 'Coverage': 100.0, 'Popularity': 4.38938}\n",
    "Func worker, run time: 18.35742425918579\n",
    "Func splitData, run time: 2.0330629348754883\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 0.62, 'Recall': 0.3, 'Coverage': 100.0, 'Popularity': 4.393025}\n",
    "Func worker, run time: 22.459643840789795\n",
    "Func splitData, run time: 2.0501880645751953\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 0.61, 'Recall': 0.3, 'Coverage': 100.0, 'Popularity': 4.393217}\n",
    "Func worker, run time: 23.829069137573242\n",
    "Func splitData, run time: 1.952528953552246\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 0.57, 'Recall': 0.27, 'Coverage': 100.03, 'Popularity': 4.388441}\n",
    "Func worker, run time: 21.796540021896362\n",
    "Func splitData, run time: 2.1322124004364014\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 0.61, 'Recall': 0.29, 'Coverage': 100.0, 'Popularity': 4.382586}\n",
    "Func worker, run time: 19.419902801513672\n",
    "Func splitData, run time: 1.9659440517425537\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 0.61, 'Recall': 0.29, 'Coverage': 100.0, 'Popularity': 4.390586}\n",
    "Func worker, run time: 18.834516286849976\n",
    "Average Result (M=8, K=0, N=10): {'Precision': 0.61, 'Recall': 0.29125, 'Coverage': 100.0075, 'Popularity': 4.389584875000001}\n",
    "Func run, run time: 185.54872608184814\n",
    "\n",
    "2. MostPopular实验（复制第二章的结果）\n",
    "Func loadData, run time: 1.403282880783081\n",
    "Func splitData, run time: 1.9211320877075195\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 12.85, 'Recall': 6.17, 'Coverage': 2.47, 'Popularity': 7.724273}\n",
    "Func worker, run time: 10.972801923751831\n",
    "Func splitData, run time: 1.9256069660186768\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 13.07, 'Recall': 6.26, 'Coverage': 2.28, 'Popularity': 7.721385}\n",
    "Func worker, run time: 10.841933012008667\n",
    "Func splitData, run time: 1.910295009613037\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 12.89, 'Recall': 6.16, 'Coverage': 2.44, 'Popularity': 7.722067}\n",
    "Func worker, run time: 10.727141857147217\n",
    "Func splitData, run time: 1.882903814315796\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 12.81, 'Recall': 6.15, 'Coverage': 2.49, 'Popularity': 7.723152}\n",
    "Func worker, run time: 10.670467138290405\n",
    "Func splitData, run time: 1.918154001235962\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 12.7, 'Recall': 6.11, 'Coverage': 2.47, 'Popularity': 7.724644}\n",
    "Func worker, run time: 10.960633993148804\n",
    "Func splitData, run time: 1.9205529689788818\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 12.9, 'Recall': 6.22, 'Coverage': 2.38, 'Popularity': 7.7234}\n",
    "Func worker, run time: 10.842862129211426\n",
    "Func splitData, run time: 1.9104499816894531\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 12.91, 'Recall': 6.21, 'Coverage': 2.47, 'Popularity': 7.721658}\n",
    "Func worker, run time: 10.716413974761963\n",
    "Func splitData, run time: 1.9528350830078125\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 12.53, 'Recall': 6.01, 'Coverage': 2.41, 'Popularity': 7.725531}\n",
    "Func worker, run time: 10.732755184173584\n",
    "Average Result (M=8, K=0, N=10): {'Precision': 12.832500000000001, 'Recall': 6.16125, 'Coverage': 2.42625, 'Popularity': 7.723263749999999}\n",
    "Func run, run time: 103.3697898387909\n",
    "\n",
    "3. ItemCF实验（复制第二章的结果）\n",
    "Func loadData, run time: 1.2348299026489258\n",
    "Func splitData, run time: 1.8201029300689697\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 22.01, 'Recall': 10.57, 'Coverage': 19.35, 'Popularity': 7.248504}\n",
    "Func worker, run time: 104.0655460357666\n",
    "Func splitData, run time: 1.8287677764892578\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 22.12, 'Recall': 10.59, 'Coverage': 18.95, 'Popularity': 7.244242}\n",
    "Func worker, run time: 103.43892693519592\n",
    "Func splitData, run time: 1.804075002670288\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 22.59, 'Recall': 10.8, 'Coverage': 19.19, 'Popularity': 7.245515}\n",
    "Func worker, run time: 103.44988584518433\n",
    "Func splitData, run time: 1.7733349800109863\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 22.02, 'Recall': 10.58, 'Coverage': 19.37, 'Popularity': 7.245227}\n",
    "Func worker, run time: 104.05003190040588\n",
    "Func splitData, run time: 1.8094689846038818\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 22.11, 'Recall': 10.63, 'Coverage': 19.33, 'Popularity': 7.260709}\n",
    "Func worker, run time: 100.68873810768127\n",
    "Func splitData, run time: 1.7294957637786865\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 22.17, 'Recall': 10.69, 'Coverage': 19.02, 'Popularity': 7.251251}\n",
    "Func worker, run time: 101.01811790466309\n",
    "Func splitData, run time: 1.73459792137146\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 22.4, 'Recall': 10.77, 'Coverage': 18.48, 'Popularity': 7.24112}\n",
    "Func worker, run time: 101.37971901893616\n",
    "Func splitData, run time: 1.7321960926055908\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 21.98, 'Recall': 10.54, 'Coverage': 19.18, 'Popularity': 7.259772}\n",
    "Func worker, run time: 101.52781391143799\n",
    "Average Result (M=8, K=10, N=10): {'Precision': 22.174999999999997, 'Recall': 10.646249999999998, 'Coverage': 19.10875, 'Popularity': 7.2495425}\n",
    "Func run, run time: 835.2476677894592\n",
    "\n",
    "4. ContentItemKNN实验\n",
    "Func loadData, run time: 1.8746070861816406\n",
    "Func splitData, run time: 2.308419942855835\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 1.65, 'Recall': 0.79, 'Coverage': 17.13, 'Popularity': 4.616362}\n",
    "Func worker, run time: 8.00853180885315\n",
    "Func splitData, run time: 2.2228479385375977\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 1.81, 'Recall': 0.87, 'Coverage': 16.81, 'Popularity': 4.611126}\n",
    "Func worker, run time: 6.897377967834473\n",
    "Func splitData, run time: 1.8748629093170166\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 1.9, 'Recall': 0.91, 'Coverage': 16.29, 'Popularity': 4.621031}\n",
    "Func worker, run time: 6.969089031219482\n",
    "Func splitData, run time: 1.9319438934326172\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 1.74, 'Recall': 0.84, 'Coverage': 16.63, 'Popularity': 4.623422}\n",
    "Func worker, run time: 7.836109161376953\n",
    "Func splitData, run time: 1.8041598796844482\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 1.79, 'Recall': 0.86, 'Coverage': 16.31, 'Popularity': 4.619975}\n",
    "Func worker, run time: 9.520605087280273\n",
    "Func splitData, run time: 1.8084988594055176\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 1.84, 'Recall': 0.89, 'Coverage': 16.47, 'Popularity': 4.638201}\n",
    "Func worker, run time: 6.670089960098267\n",
    "Func splitData, run time: 1.8033149242401123\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 1.82, 'Recall': 0.88, 'Coverage': 17.13, 'Popularity': 4.607126}\n",
    "Func worker, run time: 6.848791122436523\n",
    "Func splitData, run time: 1.8024988174438477\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 1.84, 'Recall': 0.88, 'Coverage': 16.64, 'Popularity': 4.607283}\n",
    "Func worker, run time: 6.614134073257446\n",
    "Average Result (M=8, N=10, K=10): {'Precision': 1.79875, 'Recall': 0.865, 'Coverage': 16.67625, 'Popularity': 4.61806575}\n",
    "Func run, run time: 76.9517409801483"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
